﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public enum FASTSEGMENTATION_MODE
    {
        /// <summary>
        /// 
        /// </summary>
        VERTICAL,
        /// <summary>
        /// 
        /// </summary>
        HORIZONTAL,
        /// <summary>
        /// 
        /// </summary>
        BOTH
    }
    
    //TODO: relativen modus hinzufügen

    //TODO: als Scanline blob Extraktor algorithmus benutzen,
    //dazu müssen nur die Scanlines mit ähnlichen Durchschnittsfarben die sich berühren,
    //gemerged werden. Interessante Frage dazu: Regions oder Rectangles benutzen?
    //Regions sind eigentlich besser, da beliebige Formen entstehen können.
    
    //Neue Idee: es werden wegen des Scanline Approaches überhaupt keine Rectangles oder Regions benutzt
    //sondern komplett eigene Objekte

    /// <summary>
    /// 
    /// </summary>
    public class FastSegmentation : BaseImageFilter
    {
        protected FASTSEGMENTATION_MODE MODE = FASTSEGMENTATION_MODE.BOTH;
        protected int m_nThreshold = 0;

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation"/> class.
        /// </summary>
        public FastSegmentation()
            : this(FASTSEGMENTATION_MODE.HORIZONTAL, 10)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation"/> class.
        /// </summary>
        /// <param name="_MODE">The _ MODE.</param>
        /// <param name="_nThreshold">The _n threshold.</param>
        public FastSegmentation(FASTSEGMENTATION_MODE _MODE, int _nThreshold)
        {
            MODE = FASTSEGMENTATION_MODE.BOTH;
            m_nThreshold = _nThreshold;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation"/> class.
        /// </summary>
        /// <param name="_fastSegmentation">The _fast segmentation.</param>
        internal FastSegmentation(FastSegmentation _fastSegmentation)
        {
            this.MODE = _fastSegmentation.MODE;
            this.m_nThreshold = _fastSegmentation.m_nThreshold;
        }

        /// <summary>
        /// Gets or sets the mode.
        /// </summary>
        /// <value>The mode.</value>
        public FASTSEGMENTATION_MODE Mode
        {
            get { return MODE; }
            set { MODE = value; }
        }

        /// <summary>
        /// This is a fast color segmentation on a horizontal line base
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        /// <remarks>Interesting feature to add: relative color tolerance from previous pixel
        /// Maybe I should also add a vertical mode?
        /// And what might happen if both is combined?</remarks>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            switch (MODE)
            {
                case FASTSEGMENTATION_MODE.HORIZONTAL:
                    Horizontal(_bitmap, m_nThreshold);
                    break;

                case FASTSEGMENTATION_MODE.VERTICAL:
                    Vertical(_bitmap, m_nThreshold);
                    break;

                default:
                    Horizontal(_bitmap, m_nThreshold);
                    Vertical(_bitmap, m_nThreshold);
                    break;
            }
            return _bitmap;
        }

        /// <summary>
        /// Horizontals the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="_nThreshold">The _n threshold.</param>
        /// <returns></returns>
        public static List<List<Range>> Horizontal(UnsafeBitmap _bitmap, int _nThreshold)
        {
            UnsafeBitmap bitmap = _bitmap;
            List<List<Range>> aScanLine = new List<List<Range>>();

            List<Color> aColor = new List<Color>();
            aColor.Add(bitmap.GetPixel(0, 0));

            int nCount = 1;
            int x = 0; int y = 0;
            Color colorStart = Color.Transparent;
            Color colorNext = Color.Transparent;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            Color colorAvg = Color.Transparent;
            for (y = 1; y < nHeight; y++)
            {
                List<Range> aList = new List<Range>();
                aColor.Add(bitmap.GetPixel(0, y));
                nCount = 1;

                for (x = 0; x < nWidth - 1; x++)
                {
                    colorStart = bitmap.GetPixel(x, y);
                    x++;
                    colorNext = bitmap.GetPixel(x, y);

                    int nStartX = 0;
                    int nEndX = 0;

                    //Found something interesting?
                    if (ColorFunctions.Distance2(colorStart, colorNext) <= _nThreshold)
                    {
                        //if so, remember position and search for the end of the interesting pixel formation
                        nStartX = x;
                        while (ColorFunctions.Distance2(colorStart, colorNext) <= _nThreshold)
                        {
                            if (x >= nWidth - 1)
                            {
                                break;
                            }
                            colorNext = bitmap.GetPixel(x, y);
                            aColor.Add(colorNext);
                            nCount++;
                            x++;
                        }
                        x -= 1;

                        //end found? then calculate average color and add this range to the scanlines 
                        nEndX = x;

                        //TODO: Fastsegmentation, This could be sped up
                        colorAvg = ColorFunctions.AverageColor(aColor);
                        aColor.Clear();

                        Range range = new Range(nStartX, nEndX, y, colorAvg);
                        aList.Add(range);
                    }
                    nCount++;
                }
                aScanLine.Add(aList);
            }

            List<Range> aRange = null;
            for (y = 0; y < aScanLine.Count; y++)
            {
                aRange = (List<Range>)aScanLine[y];
                foreach (Range range in aRange)
                {
                    for (int i = range.StartX; i <= range.EndX; i++)
                    {
                        bitmap.SetPixel(x, i, range.Color);
                    }
                }
            }

            return aScanLine;
        }

        /// <summary>
        /// Verticals the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="_nThreshold">The _n threshold.</param>
        /// <returns></returns>
        public static List<List<Range>> Vertical(UnsafeBitmap _bitmap, int _nThreshold)
        {
            UnsafeBitmap bitmap = _bitmap;
            List<List<Range>> aScanLine = new List<List<Range>>();

            List<Color> aColor = new List<Color>();
            aColor.Add(bitmap.GetPixel(0, 0));

            int nCount = 1;
            int x = 0; int y = 0;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            Color colorStart = Color.Transparent;
            Color colorNext = Color.Transparent;

            for (x = 0; x < nWidth; x++)
            {
                List<Range> aList = new List<Range>();
                aColor.Add(bitmap.GetPixel(x, 0));
                nCount = 1;

                for (y = 0; y < nHeight - 1; y++)
                {
                    colorStart = bitmap.GetPixel(x, y);

                    y++;
                    colorNext = bitmap.GetPixel(x, y);

                    int nStartY = 0;
                    int nEndY = 0;

                    //Found something interesting?
                    if (ColorFunctions.Distance2(colorStart, colorNext) <= _nThreshold)
                    {
                        //if so, remember position and search for the end of the interesting pixel formation
                        nStartY = y;
                        while (ColorFunctions.Distance2(colorStart, colorNext) <=_nThreshold)
                        {
                            colorNext = bitmap.GetPixel(x, y);
                            aColor.Add(colorNext);
                            y++;
                            if (y >= bitmap.Height)
                            {
                                break;
                            }
                            nCount++;
                        }

                        //end found? then calculate average color and add this range to the scanlines 
                        nEndY = y;
                        Color colorAvg = ColorFunctions.AverageColor(aColor);
                        aColor.Clear();

                        Range range = new Range(nStartY, nEndY, x, colorAvg);
                        aList.Add(range);
                    }
                    nCount++;
                }
                aScanLine.Add(aList);
            }

            //Replace values by new Colors (the colors have been derived from scanning
            //in the previous loop
            List<Range> aRange = null;
            for (x = 0; x < aScanLine.Count; x++)
            {
                aRange = (List<Range>)aScanLine[x];
                foreach (Range range in aRange)
                {
                    for (int i = range.StartX; i <= range.EndX; i++)
                    {
                        bitmap.SetPixel(x, i, range.Color);
                    }
                    //graphics.DrawLine(new Pen(range.Color), x, range.StartX, x, range.EndX);
                }
            }
            return aScanLine;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new FastSegmentation(this);
        }
    }
}
